package com.laity.unsafe;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author: Laity
 * @Project: JavaLaity
 * @Package: com.laity.unsafe.ListTest
 * @Date: 2023年11月25日 21:20
 * @Description: 集合类不安全 - List不安全
 */

public class ListTest {
    public static void main(String[] args) {
        List<String> list = new CopyOnWriteArrayList<>();
        // List<String> list = Collections.synchronizedList(arrayList);
        // List<String> list = new Vector<>();
        // List<String> list = new ArrayList<>();

        for (int i = 0; i < 10; i++) {
            // 单线程下确实很安全，所以模拟下多下次
            // arrayList.add(UUID.randomUUID().toString().substring(0, 5));
            // System.out.println(arrayList);

            // 多线程下：异常
            /*
            Exception in thread "5" Exception in thread "1" Exception in thread "7" Exception in thread "0" java.util.ConcurrentModificationException
            java.util.ConcurrentModificationException 只要并发下，集合里面都会出现这个问题
            ConcurrentModificationException => 并发修改异常
             */
            // 并发下ArrayList是不安全的
            /*
            解决方案：
            1、将 new ArrayList<>(); 换为 new Vector<>(); 不好
            2、使用工具类 List<String> list = Collections.synchronizedList(arrayList);
            3、使用JUC中的 List<String> list = new CopyOnWriteArrayList<>();
                - private transient volatile Object[] array;
                volatile：使用 volatile 修饰 instance 可以确保在多线程环境下，一个线程对 instance 的修改对其他线程是可见的。
                - CopyOnWrite：写入时复制,COW 计算机程序设计领域的一种优化策略；
                    - list被多个线程调用的时候，读取到的是固定的，写入的时候避免其它线程将数据覆盖，造成数据问题(脏读)；
                    - 就是一种读写分离的思想，写入的时候复制出来，写入完再插回，保证线程安全
                - CopyOnWriteArrayList 比 Vector
                    - Vector效率低(只要使用了Synchronized就会效率低)
                    - CopyOnWriteArrayList使用的是Lock锁
             */
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(Thread.currentThread().getName() + list);
            }, String.valueOf(i)).start();
        }
    }
}
